---
sidebar_position: 4
---
import BuiltInColumns from '../src/demos/builtInColumns'
import {ColumnsSizing} from '../src/demos/sizing'

# Columns

DSG is column based, meaning that all cells of a given column are of the same type and have the same widget.
This is why it is more like Notion or Airtable, and less like Excel.

## The columns array

Columns are simple objects, and the list of columns is a simple array of column objects:
```js
const columns = [{}, {}]
```

If you are using typescript:
```tsx
import { Column } from 'react-datasheet-grid'

type Row = { 
  firstName: string
  lastName: string 
}

const columns: Column<Row>[] = [{}, {}]
```

## Built-in columns

To get you started, 6 simple columns are built-in:
- `textColumn`
- `checkboxColumn`
- `intColumn`
- `floatColumn`
- `dateColumn`
- `isoDateColumn`
- `percentColumn`

<BuiltInColumns />

Each built-in column handles rows of a very specific data type. For instance the `textColumn` expects all rows to be strings:

```tsx
import {
  DataSheetGrid,
  textColumn,
} from 'react-datasheet-grid'

const ExampleStrings = () => {
  const [ data, setData ] = useState(['foo', 'bar', 'baz'])

  const columns = [textColumn]

  return (
    <DataSheetGrid
      value={data}
      onChange={setData}
      columns={columns}
    />
  )
}
```
And the `checkboxColumn` expects all rows to be booleans:

```tsx
import {
  DataSheetGrid,
  checkboxColumn,
} from 'react-datasheet-grid'

const ExampleBooleans = () => {
  const [ data, setData ] = useState([true, false, true])

  const columns = [checkboxColumn]

  return (
    <DataSheetGrid
      value={data}
      onChange={setData}
      columns={columns}
    />
  )
}
```

This means that columns are very simple, we will see in the next sections how to integrate them in more complex scenarios.

## Overriding column props
Because columns are simple objects, you can easily override any property by spreading it.
See the [exhaustive list](./api-reference/columns.mdx) of properties a column has.

```tsx
const columns = [
  {
    ...textColumn,
    title: 'Name',
    minWidth: 200
  }
]
```

This is a great way to factor common behaviors across multiple columns without repeating code.

## Using objects as rows
In a real world project rows often are objects, and we would like each column to handle
a specific key of that object. This is where `keyColumn` comes in:

```tsx
import {
  DataSheetGrid,
  checkboxColumn,
  textColumn,
  keyColumn,
} from 'react-datasheet-grid'

const Example = () => {
  const [ data, setData ] = useState([
    { active: true, firstName: 'Elon', lastName: 'Musk' },
    { active: false, firstName: 'Jeff', lastName: 'Bezos' },
  ])

  const columns = [
    keyColumn('active', checkboxColumn),
    keyColumn('firstName', textColumn),
    keyColumn('lastName', textColumn),
  ]

  return (
    <DataSheetGrid
      value={data}
      onChange={setData}
      columns={columns}
    />
  )
}
```

`keyColumn` wraps all properties of a column and allows you to write simpler columns that don't need to worry about an entire row object. 
It also has a very positive [performance impact](performance/optimization-guidelines#use-keycolumn).

Because columns wrapped with `keyColumn` are still regular objects, you can override any props by spreading it:

```tsx
const columns = [
  {
    ...keyColumn('active', checkboxColumn),
    title: 'Active'
  },
  {
    ...keyColumn('firstName', textColumn),
    title: 'First name'
  },
  {
    ...keyColumn('lastName', textColumn),
    title: 'Last name'
  },
]
```

### Typescript
`keyColumn` is a generic that takes the row type and the key as parameters:
```tsx
const columns = [
  {
    ...keyColumn<Row, 'active'>('active', checkboxColumn),
    title: 'Active'
  },
  // ...
]
```

Unfortunately, due to the complexity of the type, you have to type the key twice: once for the generic type, and once for the parameter (eg. `'active'` is written twice).

## Responsive width
Columns are responsive by design. DSG uses the [flexbox algorithm](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Flexible_Box_Layout/Basic_Concepts_of_Flexbox#properties_applied_to_flex_items) to dynamically compute the width of each column.

By default, all columns have the same width and a min-width of 100:
<ColumnsSizing columns={[{}, {}, {}]} />

You can use the `grow` property to make a specific column grow faster. Here the middle column is twice as large as other columns:
<ColumnsSizing columns={[{}, { grow: 2 }, {}]} />

You can pass `grow: 0` to prevent a column from growing:
<ColumnsSizing columns={[{}, { grow: 0 }, {}]} />

Use `minWidth` to prevent columns from shrinking too much. By default columns already have `minWidth: 100`:
<ColumnsSizing columns={[{}, { minWidth: 200 }, {}]} />

Read more about each property in the [API reference](./api-reference/columns#sizing).

## Building custom columns
Built-in columns are there to get your project started, but often you end up building you own custom columns
to fit your needs. Note that built-in columns and helpers have nothing special, they are built on top
of this library and do not ofer any additional feature that would not be available otherwise.

### Text-based columns
To create a text-based column that simply has some custom parsing and formatting functions you do not have
to implement a custom component from scratch and can instead use `createTextColumn`:

```tsx
import { createTextColumn } from 'react-datasheet-grid'

const column = createTextColumn({ /* options */ })

// When using Typescript you may specify the data type:
const column = createTextColumn<number>({ /* options */ })
```

The `createTextColumn` function returns a column object that can be used normally:

```tsx
const columns = [
  {
    ...keyColumn(
      'name',
      createTextColumn({
        placeholder: 'Name of the person',
        alignRight: true,
        //...
      }) 
    ),
    title: 'Name'
  },
]
```

You can also create a custom re-usable column instead of inline it:
```tsx
// columns/email.tsx
export const emailColumn = createTextColumn({ /* Custom parser & formatter for emails */ })

// index.tsx
import { emailColumn } from './columns/email'

const columns = [
  {
    ...keyColumn('email', emailColumn),
    title: 'Email'
  },
]
```

#### placeholder
> Type: `string`<br />
> Default: `null`

#### alignRight
> Type: `boolean`<br />
> Default: `false`

#### continuousUpdates
> Type: `boolean`<br />
> Default: `true`

When true, data is updated as the user types, otherwise it is only updated on blur.
Setting it to `false` will improve performance.

#### deletedValue
> Type: `T`<br />
> Default: `null`

Value to use when deleting the cell

#### parseUserInput
> Type: `(value: string) => T`

Parse what the user types

#### formatBlurredInput
> Type: `(value: T) => string`

Format the value of the input when it is blurred

#### formatInputOnFocus
> Type: `(value: T) => string`

Format the value of the input when it gets focused

#### formatForCopy
> Type: `(value: T) => string`

Format the value when copy

#### parsePastedValue
> Type: `(value: string) => T`

Parse the pasted value

### Using a custom component

If you need a more exotic column, you can write your own from scratch. A column consists of a custom component
that renders the widget and a few props to control its behavior.

See a [step-by-step example](./examples/implementing-select) of how to implement a select column, or read the
[exhaustive list](./api-reference/columns.mdx) of properties a column has.

```tsx
const columns = [
  {
    component: MyWidget,
    title: 'My column'
  }
]
```
